package com.jnevision.util;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.poi.EncryptedDocumentException;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;

/**
 * 操作Excel的类
 * 
 * @author Gavin
 * @date 2018/06/13
 */
public class Excels {

    private static final int DATA_START_ROW_NUM = 1;

    /**
     * 
     * Examples: <blockquote>
     * 
     * <pre>
     * InputStream input = Excels.class.getResourceAsStream("area.xlsx");
     * LinkedHashMap<String, String> title = new LinkedHashMap<>();
     * title.put("名称", "name");
     * title.put("地区编码", "adcode");
     * title.put("城市编码", "citycode");
     * title.put("上级id", "parentId");
     * List<Amap> amap = read(input, title, Amap.class);
     * </pre>
     * 
     * </blockquote>
     * 
     * @param input 文件流
     * @param title <"名称", "name">;
     * @param clazz 实体类型
     * @return 实体类型的list集合
     */
    public static <T> List<T> read(InputStream input, LinkedHashMap<String, String> title, Class<T> clazz) {
        try {
            Workbook wb = WorkbookFactory.create(input);

            Sheet st = wb.getSheetAt(0);

            return read(st, title, clazz);
        } catch (EncryptedDocumentException e) {
            e.printStackTrace();
        } catch (InvalidFormatException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * Examples: <blockquote>
     * 
     * <pre>
     * InputStream input = Excels.class.getResourceAsStream("area.xlsx");
     * Workbook wb = WorkbookFactory.create(input);
     * Sheet sheet = wb.getSheetAt(0);
     * LinkedHashMap<String, String> title = new LinkedHashMap<>();
     * title.put("名称", "name");
     * title.put("地区编码", "adcode");
     * title.put("城市编码", "citycode");
     * title.put("上级id", "parentId");
     * List<Amap> amap = read(sheet, title, Amap.class);
     * </pre>
     * 
     * </blockquote>
     * 
     * @param sheet excel中的sheet页
     * @param readField Excel和实体类对应的字段，即<"名称", "name">;
     * @param clazz 实体类
     * @return 实体类的list集合
     */
    public static <T> List<T> read(Sheet sheet, LinkedHashMap<String, String> readField, Class<T> clazz) {
        final List<T> list = new ArrayList<>();

        try {
            Field[] fields = clazz.getDeclaredFields();
            Map<String, Field> title = new HashMap<>();

            for (Field fd : fields) {
                title.put(fd.getName(), fd);
            }

            Iterator<Row> rowIt = sheet.iterator();

            while (rowIt.hasNext()) {
                Row row = rowIt.next();

                if (row == null || row.getRowNum() < DATA_START_ROW_NUM) {
                    continue;
                }

                T t1 = clazz.newInstance();

                try {
                    String[] exT = readField.keySet().toArray(new String[readField.size()]);
                    for (int j = 0; j < exT.length; j++) {
                        Method method =
                            clazz.getDeclaredMethod("set" + readField.get(exT[j]).substring(0, 1).toUpperCase()
                                + readField.get(exT[j]).substring(1), title.get(readField.get(exT[j])).getType());
                        Cell cell = row.getCell(j);

                        if (cell == null) {
                            continue;
                        }

                        Object obj = null;
                        switch (title.get(readField.get(exT[j])).getType().getName()) {
                            case "int":
                            case "java.lang.Integer":
                                obj = (int)(cell.getNumericCellValue());
                                break;
                            case "byte":
                            case "java.lang.Byte":
                                obj = (byte)(cell.getNumericCellValue());
                                break;
                            case "short":
                            case "java.lang.Short":
                                obj = (short)(cell.getNumericCellValue());
                                break;
                            case "long":
                            case "java.lang.Long":
                                obj = (long)(cell.getNumericCellValue());
                                break;
                            case "float":
                            case "java.lang.Float":
                                obj = (float)(cell.getNumericCellValue());
                                break;
                            case "double":
                            case "java.lang.Double":
                                obj = cell.getNumericCellValue();
                                break;
                            case "char":
                            case "java.lang.Character":
                                obj = cell.getStringCellValue();
                                break;
                            case "boolean":
                            case "java.lang.Boolean":
                                obj = cell.getBooleanCellValue();
                                break;
                            case "java.lang.String":
                                obj = cell.getStringCellValue();
                                break;

                            default:
                                obj = cell.getStringCellValue();
                                break;
                        }

                        method.invoke(t1, obj);
                    }

                    list.add(t1);
                } catch (Exception e) {
                    e.printStackTrace();
                    return Collections.emptyList();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return list;
    }
}
